#include "graphics_util.h"
#include "math/rect.h"
#include "util/mmgr.h"		
#include "util/timer.h"		
#include "math/math_core.h"
#include "math/facing.h"
	
#include <cassert>
#include <math.h>

//float		GraphicsUtil::InvW = 1.0f/800.0f;
//float		GraphicsUtil::InvH = 1.0f/600.0f;
//unsigned int	GraphicsUtil::WIDTH = 800;
//unsigned int	GraphicsUtil::HEIGHT = 600;
//bool		GraphicsUtil::SnapToPixel = false;
//float		GraphicsUtil::H2W = 600.0f/800.0f;
//float		GraphicsUtil::W2H = 800.0f/600.0f;

float		GraphicsUtil::InvW = 1.0f/640.0f;
float		GraphicsUtil::InvH = 1.0f/960.0f;
unsigned int	GraphicsUtil::WIDTH = 640;
unsigned int	GraphicsUtil::HEIGHT = 960;
bool		GraphicsUtil::SnapToPixel = false;
float		GraphicsUtil::H2W = 960.0f/640.0f;
float		GraphicsUtil::W2H = 640.0f/960.0f;

#if  defined(_PLATFORM_WII) || defined(_ENABLE_OPENGL_)
	const float GraphicsUtil::PT_OFFSET = 0.0f;
#else
	const float GraphicsUtil::PT_OFFSET = 0.5f;
#endif

int	GraphicsUtil::FillQuad(Graphics::Vertex2D*	Quad, Graphics::Vertex2D*	VertA)
{
	int	VertI = 0;

    VertA[VertI++] = Quad[1];
    VertA[VertI++] = Quad[0];
    VertA[VertI++] = Quad[3];
											
    VertA[VertI++] = Quad[2];
    VertA[VertI++] = Quad[3];
    VertA[VertI++] = Quad[0];
	return VertI;
}

int	GraphicsUtil::FillQuad(math::Vec2* PosA, math::Vec2* UVA, COLOR* ColorA, Graphics::Vertex2D*	VertA)
{
	Graphics::POS_TYPE	Width = GetGraphics()->GetScreenWidth();
	Graphics::POS_TYPE	Height = GetGraphics()->GetScreenHeight();

	const COLOR		DefaultColorA[] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
	if (!ColorA)	ColorA = (COLOR*)&DefaultColorA[0];
	if (!UVA)
	{
		float	tx1	= 0.0f;
		float	ty1	= 0.0f;
		float	tx2	= 1.0f;
		float	ty2	= 1.0f;

		unsigned VertI = 0;

		VertA[VertI++].Fill((PosA[1].x*Width) - PT_OFFSET, (PosA[1].y*Height) - PT_OFFSET, ColorA[1],		tx1, ty2 );
		VertA[VertI++].Fill((PosA[0].x*Width) - PT_OFFSET, (PosA[0].y*Height) - PT_OFFSET, ColorA[0],		tx1, ty1 );
		VertA[VertI++].Fill((PosA[3].x*Width) - PT_OFFSET, (PosA[3].y*Height) - PT_OFFSET, ColorA[3],		tx2, ty2 );
															 
		VertA[VertI++].Fill((PosA[2].x*Width) - PT_OFFSET, (PosA[2].y*Height) - PT_OFFSET, ColorA[2],		tx2, ty1 );
		VertA[VertI++].Fill((PosA[3].x*Width) - PT_OFFSET, (PosA[3].y*Height) - PT_OFFSET, ColorA[3],		tx2, ty2 );
		VertA[VertI++].Fill((PosA[0].x*Width) - PT_OFFSET, (PosA[0].y*Height) - PT_OFFSET, ColorA[0],		tx1, ty1 );
	} else
	{
		unsigned VertI = 0;

		VertA[VertI++].Fill((PosA[1].x*Width) - PT_OFFSET, (PosA[1].y*Height) - PT_OFFSET, ColorA[1],		UVA[1].x, UVA[1].y );
		VertA[VertI++].Fill((PosA[0].x*Width) - PT_OFFSET, (PosA[0].y*Height) - PT_OFFSET, ColorA[0],		UVA[0].x, UVA[0].y );
		VertA[VertI++].Fill((PosA[3].x*Width) - PT_OFFSET, (PosA[3].y*Height) - PT_OFFSET, ColorA[3],		UVA[3].x, UVA[3].y);
																	
		VertA[VertI++].Fill((PosA[2].x*Width) - PT_OFFSET, (PosA[2].y*Height) - PT_OFFSET, ColorA[2],		UVA[2].x, UVA[2].y );
		VertA[VertI++].Fill((PosA[3].x*Width) - PT_OFFSET, (PosA[3].y*Height) - PT_OFFSET, ColorA[3],		UVA[3].x, UVA[3].y );
		VertA[VertI++].Fill((PosA[0].x*Width) - PT_OFFSET, (PosA[0].y*Height) - PT_OFFSET, ColorA[0],		UVA[0].x, UVA[0].y );
	}
	return 6;
}

int		GraphicsUtil::FillQuad(	const math::Vec2& _Pos, const math::Vec2& _Size, 
								const math::Vec2& _TopLeftUV,const math::Vec2& _BottomRightUV ,
								COLOR	c, 
								Graphics::Vertex2D*	VertA)
{
	Graphics::POS_TYPE	Width = GetGraphics()->GetScreenWidth();
	Graphics::POS_TYPE	Height = GetGraphics()->GetScreenHeight();
	Graphics::POS_TYPE	x = SnapToPixel ? floor(_Pos.x * Width + PT_OFFSET) : (_Pos.x * Width);
	Graphics::POS_TYPE	y = SnapToPixel ? floor(_Pos.y * Height +PT_OFFSET) : _Pos.y * Height;
	float	tx1	= _TopLeftUV.x;
	float	ty1	= _TopLeftUV.y;
	float	tx2	= _BottomRightUV.x;
	float	ty2	= _BottomRightUV.y;
	Graphics::POS_TYPE   w   = SnapToPixel ? ceil(_Size.x * Width - 0.000500f) : (_Size.x * Width);
	Graphics::POS_TYPE   h   = SnapToPixel ? ceil(_Size.y * Height - 0.000500f) : (_Size.y * Height);


    int                 VertI  = 0;
    VertA[VertI++].Fill(x+0 - PT_OFFSET,y+h - PT_OFFSET, c, tx1, ty2 );
    VertA[VertI++].Fill(x+0 - PT_OFFSET,y+0 - PT_OFFSET, c, tx1, ty1 );
    VertA[VertI++].Fill(x+w - PT_OFFSET,y+h - PT_OFFSET, c, tx2, ty2 );

    VertA[VertI++].Fill(x+w - PT_OFFSET,y+0 - PT_OFFSET, c, tx2, ty1 );
    VertA[VertI++].Fill(x+w - PT_OFFSET,y+h - PT_OFFSET, c, tx2, ty2 );
    VertA[VertI++].Fill(x+0 - PT_OFFSET,y+0 - PT_OFFSET, c, tx1, ty1 );
	return VertI;
}
int		GraphicsUtil::FillQuad(	const math::Vec2& _Pos, const math::Vec2& _Size, 
								const math::Vec2& _TopLeftUV,const math::Vec2& _BottomRightUV ,
								COLOR	c0, COLOR c1, COLOR c2, COLOR c3,
								Graphics::Vertex2D*	VertA)
{
	Graphics::POS_TYPE	Width = GetGraphics()->GetScreenWidth();
	Graphics::POS_TYPE	Height = GetGraphics()->GetScreenHeight();
	Graphics::POS_TYPE	x = SnapToPixel ? floor(_Pos.x * Width + PT_OFFSET) : (_Pos.x * Width);
	Graphics::POS_TYPE	y = SnapToPixel ? floor(_Pos.y * Height + PT_OFFSET) : _Pos.y * Height;
	float	tx1	= _TopLeftUV.x;
	float	ty1	= _TopLeftUV.y;
	float	tx2	= _BottomRightUV.x;
	float	ty2	= _BottomRightUV.y;
	Graphics::POS_TYPE   w   = SnapToPixel ? ceil(_Size.x * Width - 0.000500f) : (_Size.x * Width);
	Graphics::POS_TYPE   h   = SnapToPixel ? ceil(_Size.y * Height - 0.000500f) : (_Size.y * Height);


    int                 VertI  = 0;
    VertA[VertI++].Fill(x+0 - PT_OFFSET,y+h - PT_OFFSET, c3, tx1, ty2 );
    VertA[VertI++].Fill(x+0 - PT_OFFSET,y+0 - PT_OFFSET, c0, tx1, ty1 );
    VertA[VertI++].Fill(x+w - PT_OFFSET,y+h - PT_OFFSET, c2, tx2, ty2 );

    VertA[VertI++].Fill(x+w - PT_OFFSET,y+0 - PT_OFFSET, c1, tx2, ty1 );
    VertA[VertI++].Fill(x+w - PT_OFFSET,y+h - PT_OFFSET, c2, tx2, ty2 );
    VertA[VertI++].Fill(x+0 - PT_OFFSET,y+0 - PT_OFFSET, c0, tx1, ty1 );
	return VertI;
}



int GraphicsUtil::FillRotQuad(	const math::Vec2& _Pos, const math::Vec2& _Size, float Rot,
								const math::Vec2& _UV1,const math::Vec2& _UV2,
								COLOR	c, 
								Graphics::Vertex2D*	VertA)
{
	math::Vec2			Screen(GetGraphics()->GetScreenWidth(), GetGraphics()->GetScreenHeight());
	float				RadRotation = Rot * DEG_TO_RAD;
	math::Vec2			Height(sinf(RadRotation), sinf(RadRotation+ MATH_HALF_PI));
	math::Vec2			Width(Height.y*_Size.x*0.5f, -Height.x*_Size.x*0.5f * W2H);
	
	Height.x *=_Size.y*0.5f * H2W;
	Height.y *=_Size.y*0.5f ;

	VertA[0].Fill((_Pos.x - Width.x - Height.x) * Screen.x, (_Pos.y - Width.y - Height.y) * Screen.y, c, _UV1.x,_UV1.y); 
	VertA[1].Fill((_Pos.x + Width.x - Height.x) * Screen.x, (_Pos.y + Width.y - Height.y) * Screen.y, c, _UV2.x,_UV1.y); 
	VertA[2].Fill((_Pos.x + Width.x + Height.x) * Screen.x, (_Pos.y + Width.y + Height.y) * Screen.y, c, _UV2.x,_UV2.y); 

	VertA[3].Fill((_Pos.x - Width.x - Height.x) * Screen.x, (_Pos.y - Width.y - Height.y) * Screen.y, c, _UV1.x,_UV1.y); 
	VertA[4].Fill((_Pos.x + Width.x + Height.x) * Screen.x, (_Pos.y + Width.y + Height.y) * Screen.y, c, _UV2.x,_UV2.y); 
	VertA[5].Fill((_Pos.x - Width.x + Height.x) * Screen.x, (_Pos.y - Width.y + Height.y) * Screen.y, c, _UV1.x,_UV2.y); 
	return 6;
}



int	GraphicsUtil::FillRotatedQuad(	
							int	Rotation,
							const math::Vec2& Pos, const math::Vec2& Size, 
							const math::Vec2& UV1,const math::Vec2& UV2,
							COLOR	c, 
							Graphics::Vertex2D*	VertA)
{
	switch(Rotation)
	{
		 case 0:		// 0 degrees
			FillQuad(Pos, Size, UV1, UV2, c, VertA);
			break;
		case  1:		// 90 degrees
			{
				math::Vec2		PosA[4], UVA[4];
				unsigned long			ColorA[4] = {c, c, c, c};
				PosA[0] = Pos;
				PosA[1]	= Pos + math::Vec2(0, Size.x*W2H);
				PosA[2]	= Pos + math::Vec2(Size.y*H2W, 0);
				PosA[3]	= Pos + math::Vec2(Size.y*H2W, Size.x*W2H);
				
				UVA[1]	= math::Vec2(UV2.x, UV2.y);
				UVA[3]	= math::Vec2(UV2.x, UV1.y);
				UVA[0]	= math::Vec2(UV1.x, UV2.y);
				UVA[2]	= math::Vec2(UV1.x, UV1.y);
				FillQuad(PosA, UVA, ColorA, VertA);
//				FillRotQuad(Pos+(math::Vec2(Size.y, Size.x)*0.5f), Size, -90, UV1, UV2, c, VertA);
			}
			break;
		case  2:		// 180 degrees
			FillQuad(Pos, Size, UV2, UV1, c, VertA);
			break;
		case  3:		// 90 degrees
			{
				math::Vec2		PosA[4], UVA[4];
				unsigned long			ColorA[4] = {c, c, c, c};
				PosA[0] = Pos;
				PosA[1]	= Pos + math::Vec2(0, Size.x*W2H);
				PosA[2]	= Pos + math::Vec2(Size.y*H2W, 0);
				PosA[3]	= Pos + math::Vec2(Size.y*H2W, Size.x*W2H);
				
				UVA[1]	= math::Vec2(UV1.x, UV1.y);
				UVA[3]	= math::Vec2(UV1.x, UV2.y);
				UVA[0]	= math::Vec2(UV2.x, UV1.y);
				UVA[2]	= math::Vec2(UV2.x, UV2.y);
				FillQuad(PosA, UVA, ColorA, VertA);
				///FillRotQuad(Pos+(math::Vec2(Size.y, Size.x)*0.5f), Size, -270, UV1, UV2, c, VertA);
			}
			break;
		default:
			FillQuad(Pos, Size, UV1, UV2, c, VertA);
	}
	return 6;
}

int		GraphicsUtil::FillLine(const math::Vec2& From, const math::Vec2& To, float Radius, COLOR c0, COLOR c1,  Graphics::Vertex2D*	VertA)
{
	math::Vec2			Screen(GetGraphics()->GetScreenWidth(), GetGraphics()->GetScreenHeight());
	math::Vec2			DX(-(To.y-From.y), (To.x-From.x));
	DX.Normalize();

	int					i = 0;
	float	fx = From.x * Screen.x;
	float	fy = From.y * Screen.y;
	float	tx = To.x * Screen.x;
	float	ty = To.y * Screen.y;
	float	dx = DX.x* Radius * Screen.x* H2W;
	float	dy = DX.y* Radius *Screen.y;

	// consists of 2 triangles
	//  T-DX	T + DX
	//    |\----|
	//    | \   |
	//    |  \  |
	//    |   \ |
	//    |----\|
	//  F-DX	F + DX
	VertA[i++].Fill(tx-dx		, (ty - dy	) , c1, 1.0f,	0.5f); 
	VertA[i++].Fill(tx			, (ty		) , c1, 0.0f,	0.5f);
	VertA[i++].Fill(fx			, (fy		) , c0, 0.0f,	0.5f);

	VertA[i++].Fill(tx - dx		, (ty - dy	) , c1, 1.0f,	0.5f); 
	VertA[i++].Fill(fx			, (fy		) , c0, 0.0f,	0.5f);
	VertA[i++].Fill(fx- dx		, (fy- dy	) , c0, 1.0f,	0.5f);

	VertA[i++].Fill(tx			, (ty		) , c1, 0.0f,	0.5f);
	VertA[i++].Fill(tx	+ dx	, (ty + dy	) , c1, 1.0f,	0.5f);
	VertA[i++].Fill(fx + dx		, (fy + dy	) , c0, 1.0f,	0.5f);

	VertA[i++].Fill(tx			, (ty		) , c1, 0.0f,	0.5f);
	VertA[i++].Fill(fx + dx		, (fy + dy	) , c0, 1.0f,	0.5f);
	VertA[i++].Fill(fx			, (fy		) , c0, 0.0f,	0.5f);
	return i;
}

int		GraphicsUtil::FillLine(const math::Vec2& From, const math::Vec2& To, float Radius, COLOR c0, COLOR c1,  const math::Vec2& UV1, const math::Vec2& UV2, Graphics::Vertex2D*	VertA)
{
	math::Vec2			Screen(GetGraphics()->GetScreenWidth(), GetGraphics()->GetScreenHeight());
	math::Vec2			DX(-(To.y-From.y), (To.x-From.x));
	DX.Normalize();

	int					i = 0;
	float	fx = From.x * Screen.x;
	float	fy = From.y * Screen.y;
	float	tx = To.x * Screen.x;
	float	ty = To.y * Screen.y;
	float	dx = DX.x* Radius * Screen.x;// * W2H;
	float	dy = DX.y* Radius *Screen.y* W2H;

	float	x2 = UV1.x + ((UV2.x-UV1.x)*0.5f);


	// consists of 4 triangles
	//  T-DX	T	T + DX
	//    |\----|\----|
	//    | \   | \   |
	//    |  \  |  \  |
	//    |   \ |   \ |
	//    |----\|----\|
	//  F-DX	F	F + DX

	VertA[i++].Fill(tx-dx		, (ty - dy	) , c1, UV1.x,	UV1.y); 
	VertA[i++].Fill(tx			, (ty		) , c1, x2,		UV1.y);
	VertA[i++].Fill(fx			, (fy		) , c0, x2,		UV2.y);

	VertA[i++].Fill(tx - dx		, (ty - dy	) , c1, UV1.x,	UV1.y); 
	VertA[i++].Fill(fx			, (fy		) , c0, x2,		UV2.y);
	VertA[i++].Fill(fx- dx		, (fy- dy	) , c0, UV1.x,	UV2.y);

	VertA[i++].Fill(tx			, (ty		) , c1, x2,		UV1.y);
	VertA[i++].Fill(tx	+ dx	, (ty + dy	) , c1, UV2.x,	UV1.y);
	VertA[i++].Fill(fx + dx		, (fy + dy	) , c0, UV2.x,	UV2.y);

	VertA[i++].Fill(tx			, (ty		) , c1, x2,		UV1.y);
	VertA[i++].Fill(fx + dx		, (fy + dy	) , c0, UV2.x,	UV2.y);
	VertA[i++].Fill(fx			, (fy		) , c0, x2,		UV2.y);

	return i;
}


int	GraphicsUtil::FillPath(const math::Vec2& Offset, float Scale, const math::Vec2* LineA, int VertC, float Radius,  Graphics::Vertex2D*	VertA, int Max)
{
	int					VertI = 0;
	float				sx = GetGraphics()->GetScreenWidth()* Scale;
	float				sy = GetGraphics()->GetScreenHeight()* Scale;

	assert((VertC-1)*12 < Max);
	if ( (VertC-1) * 12 >= Max) return 0;

	float		oldx = 0;
	float		oldy = 0;
	for (int i=0; i<VertC-1; i++)
	{
		math::Vec2			From = (LineA[i]-Offset);
		math::Vec2			To = (LineA[i+1]-Offset);
		math::Vec2			DX(-(To.y-From.y), (To.x-From.x));
		DX.Normalize();
		
		float	fx = From.x * sx;
		float	fy = From.y * sy;
		float	tx = To.x * sx;
		float	ty = To.y * sy;
		float	dx = DX.x* Radius * sx ;
		float	dy = DX.y* Radius * sy ;
		
		float	Fraction0 = i/(float)VertC;
		float	Fraction1 = (i+1)/(float)VertC;
		COLOR	c1 = ColorRGBA(255, 255, 255, int(Fraction1*255));
		COLOR	c0 = ColorRGBA(255, 255, 255, int(Fraction0*255));

		VertA[VertI++].Fill(tx-dx		, (ty - dy	) , c1, 1.0f, 0.5f); 
		VertA[VertI++].Fill(tx			, (ty		) , c1, 0.0f, 0.5f);
		VertA[VertI++].Fill(fx			, (fy		) , c0, 0.0f, 0.5f);

		VertA[VertI++].Fill(tx - dx		, (ty - dy	) , c1, 1.0f, 0.5f); 
		VertA[VertI++].Fill(fx			, (fy		) , c0, 0.0f, 0.5f);
		VertA[VertI++].Fill(fx- oldx	, (fy- oldy	) , c0, 1.0f, 0.5f);

		VertA[VertI++].Fill(tx			, (ty		) , c1, 0.0f, 0.5f);
		VertA[VertI++].Fill(tx	+ dx	, (ty + dy	) , c1, 1.0f, 0.5f);
		VertA[VertI++].Fill(fx + oldx		, (fy + dy	) , c0, 1.0f, 0.5f);

		VertA[VertI++].Fill(tx			, (ty		) , c1, 0.0f, 0.5f);
		VertA[VertI++].Fill(fx + dx		, (fy + oldy	) , c0, 1.0f, 0.5f);
		VertA[VertI++].Fill(fx			, (fy		) , c0, 0.0f, 0.5f);
		oldx = dx;
		oldy = dy;
	}
	return VertI;
}

int	GraphicsUtil::FillPath(const math::Vec2& Offset, float Scale, const math::Vec2* LineA, int VertC, const math::Vec2& MinUV, const math::Vec2& MaxUV,  const math::Vec4& FromCol, const math::Vec4& ToCol, float FromW, float ToW, Graphics::Vertex2D*	VertA, int Max)
{
	int					VertI = 0;
	float				sx = GetGraphics()->GetScreenWidth();
	float				sy = GetGraphics()->GetScreenHeight();

	assert((VertC-1)*12 < Max);
	if ( (VertC-1) * 12 >= Max) return 0;

	float		oldx = 0;
	float		oldy = 0;
	
	for (int i=0; i<VertC-1; i++)
	{
		float	Fraction0 = i/(float)VertC;
		float	Fraction1 = (i+1)/(float)VertC;

		math::Vec2			From = (LineA[i]-Offset)*Scale;
		math::Vec2			To = (LineA[i+1]-Offset)*Scale;
		math::Vec2			DX(-(To.y-From.y), (To.x-From.x));
		DX.Normalize();

		float	Rad = FromW + ((ToW-FromW)*Fraction0);

		float	fx = From.x * sx;
		float	fy = From.y * sy;
		float	tx = To.x * sx;
		float	ty = To.y * sy;
		float	dx = DX.x* Rad * sx * Scale;
		float	dy = DX.y* Rad * sy * Scale;


		float	fv = MinUV.y + (Fraction0 * (MaxUV.y-MinUV.y));
		float	tv = MinUV.y + (Fraction1 * (MaxUV.y-MinUV.y));
		
		math::Vec4 col0 = FromCol + ((ToCol-FromCol) * Fraction0);
		math::Vec4 col1 = FromCol + ((ToCol-FromCol) * Fraction1);
		COLOR	c0 = ColorfRGBA(col0.x, col0.y, col0.z, col0.w);
		COLOR	c1 = ColorfRGBA(col1.x, col1.y, col1.z, col1.w);

		VertA[VertI++].Fill(tx-dx		, (ty - dy	) , c1, MaxUV.x, tv); 
		VertA[VertI++].Fill(tx			, (ty		) , c1, MinUV.x, tv);
		VertA[VertI++].Fill(fx			, (fy		) , c0, MinUV.x, fv);

		VertA[VertI++].Fill(tx - dx		, (ty - dy	) , c1, MaxUV.x, tv); 
		VertA[VertI++].Fill(fx			, (fy		) , c0, MinUV.x, fv);
		VertA[VertI++].Fill(fx- oldx	, (fy- oldy	) , c0, MaxUV.x, fv);

		VertA[VertI++].Fill(tx			, (ty		) , c1, MinUV.x, tv);
		VertA[VertI++].Fill(tx	+ dx	, (ty + dy	) , c1, MaxUV.x, tv);
		VertA[VertI++].Fill(fx + oldx	, (fy + dy	) , c0, MaxUV.x, fv);

		VertA[VertI++].Fill(tx			, (ty		) , c1, MinUV.x, tv);
		VertA[VertI++].Fill(fx + oldx	, (fy + oldy) , c0, MaxUV.x, fv);
		VertA[VertI++].Fill(fx			, (fy		) , c0, MinUV.x, fv);

		oldx = dx;
		oldy = dy;
	}
	return VertI;
}




#define CIRCLE_SEGMENTS 16
#define CIRCLE_SIN(i) (float)sinf((i / (float)( CIRCLE_SEGMENTS )) * MATH_2PI)
#define CIRCLE_COS(i) (float)cosf((i / (float)( CIRCLE_SEGMENTS )) * MATH_2PI)
static const	float	CircleX[] =
{
	CIRCLE_COS(0), 
	CIRCLE_COS(1), 
	CIRCLE_COS(2), 
	CIRCLE_COS(3), 
	CIRCLE_COS(4), 
	CIRCLE_COS(5), 
	CIRCLE_COS(6), 
	CIRCLE_COS(7), 
	CIRCLE_COS(8), 
	CIRCLE_COS(9), 
	CIRCLE_COS(10),
	CIRCLE_COS(11),
	CIRCLE_COS(12),
	CIRCLE_COS(13),
	CIRCLE_COS(14),
	CIRCLE_COS(15),
	CIRCLE_COS(16)
};

static const	float	CircleY[] =
{
	CIRCLE_SIN(0), 
	CIRCLE_SIN(1), 
	CIRCLE_SIN(2), 
	CIRCLE_SIN(3), 
	CIRCLE_SIN(4), 
	CIRCLE_SIN(5), 
	CIRCLE_SIN(6), 
	CIRCLE_SIN(7), 
	CIRCLE_SIN(8), 
	CIRCLE_SIN(9), 
	CIRCLE_SIN(10),
	CIRCLE_SIN(11),
	CIRCLE_SIN(12),
	CIRCLE_SIN(13),
	CIRCLE_SIN(14),
	CIRCLE_SIN(15),
	CIRCLE_SIN(16)
};

int		GraphicsUtil::FillCapsule(const math::Vec2& From, const math::Vec2& To, float Radius, COLOR c0, COLOR c1,  Graphics::Vertex2D*	VertA)
{
	// draw line
	math::Vec2	Delta = To-From;
	math::Vec2	P = From + (Delta*0.5f);
	int		Vert = FillRotQuad(P, math::Vec2(Radius, Delta.Magnitude()), math::Facing::AngleFromTo(From, To), math::Vec2(0,0), math::Vec2(1,1), c0, VertA);
//	int		Vert = FillLine(From, To, Radius, c0, c1, VertA);
	// draw end-caps later
	return Vert;
}

int	GraphicsUtil::FillCircle(const math::Vec2& Pos, float Radius, COLOR c0, COLOR c1,  Graphics::Vertex2D*	VertA)
{
	int					Vert = 0;
	float				sx = GetGraphics()->GetScreenWidth();
	float				sy = GetGraphics()->GetScreenHeight();
	float				rx = sx * Radius;
	float				ry = sy * Radius;
	float				px = Pos.x * sx;
	float				py = Pos.y * sy;
	for (int i=0; i<CIRCLE_SEGMENTS; i++)
	{
		VertA[Vert++].Fill(px, py, c0, 0.01f, 0.5f); 
		VertA[Vert++].Fill(px + (CircleX[i]*rx),	py+ (CircleY[i]*ry)*W2H, c1, 0.99f, 0.5f); 
		VertA[Vert++].Fill(px + (CircleX[i+1]*rx),	py+ (CircleY[i+1]*ry)*W2H, c1, 0.99f, 0.5f); 
	}
	return Vert;
}

int	GraphicsUtil::FillSpline(const math::Vec2& Offset, float Scale, math::SplineCache2D* pSpline, float UVOffset, float UVMult,  float Width, Graphics::Vertex2D*	VertA, int Max)
{
	if (!pSpline) return 0;
	float				sx = GetGraphics()->GetScreenWidth();
	float				sy = GetGraphics()->GetScreenHeight();
	float				v0, v1;
	UVOffset -= floorf(UVOffset);	// cap it to 0..1

	math::Vec2 PV1, PV2, PV3, PV4;
	math::Vec2 From, To;
	math::Vec2 Normal, LastNormal;
	float s;
	float inv_segments = 1.0f / float(pSpline->GetDrawSegments());
	unsigned int NodeC = (unsigned int)pSpline->GetNodeC();
	int		Index = 0;
	if (NodeC < 2) return 0;
	for (unsigned int i = 0; i < (NodeC - 1); i++)
	{
		if (i == 0)
			PV1 = pSpline->GetNode(i)->Pos;
		else
			PV1 = pSpline->GetNode(i-1)->Pos;

		PV2 = pSpline->GetNode(i)->Pos;
		PV3 = pSpline->GetNode(i+1)->Pos;

		if ((i + 2) >= NodeC)
			PV4 = PV3;
		else
			PV4 = pSpline->GetNode(i+2)->Pos;

		s = 0.0f;
		From = PV2;

		// assume that the UV multipler is for a length of 1
		// we then multiply it by the actual length to get a 
		// weighted distance. It's not 100% correct, but 
		// probably better than not weighting it
		float	SegmentLength = (PV3-PV2).Magnitude();

		for (int j = 0; j < (int)pSpline->GetDrawSegments(); j++)
		{
			float s0 = float(j+0) * inv_segments;
			float s1 = float(j+1) * inv_segments;
			pSpline->Interpolate(To, PV1, PV2, PV3, PV4, s1);

//			v0 = UVOffset + (s0 * UVMult*SegmentLength);
//			v1 = UVOffset + (s1 * UVMult*SegmentLength);
			v0 = UVOffset + (s0 * UVMult);
			v1 = UVOffset + (s1 * UVMult);

			Normal.x = (-(To.y-From.y));
			Normal.y = ( (To.x-From.x));
			Normal.Normalize();
			if (i == 0 && j == 0) LastNormal	= Normal;
//			v0 = UVOffset + ((j/float(pSpline->GetDrawSegments()))*UVMult*SegmentLength);
//			v1 = UVOffset + (((j+1)/float(pSpline->GetDrawSegments()))*UVMult*SegmentLength);
			COLOR	c = 0xFFFFFFFF;

			float	fx = (From.x- Offset.x) * sx * Scale;
			float	fy = (From.y- Offset.y) * sy * Scale;
			float	tx = (To.x- Offset.x) * sx * Scale;
			float	ty = (To.y- Offset.y) * sy * Scale;
			float	cx = Normal.x* Width * sx * Scale;
			float	cy = Normal.y* Width * sy * Scale;
			float	lx = LastNormal.x* Width * sx* Scale;
			float	ly = LastNormal.y* Width * sy* Scale;
		

			VertA[Index++].Fill(tx - cx,		ty - cy ,  c, 0, v0); 
			VertA[Index++].Fill(tx + cx,		ty + cy ,  c, 1, v0); 
			VertA[Index++].Fill(fx - lx,		fy - ly ,  c, 0, v1); 


			VertA[Index++].Fill(tx + cx,		ty + cy ,  c, 1, v0); 
			VertA[Index++].Fill(fx + lx,		fy + ly ,  c, 1, v1); 
			VertA[Index++].Fill(fx - lx,		fy - ly ,  c, 0, v1); 

			From = To;
			LastNormal	= Normal;

			if (Index + 6 > Max) return Index;	// safty
		}
	//	UVOffset = v0;
	}
	return Index;
}

bool GraphicsUtil::CopyBufferRect(void* pBuffer, const void* pSrc, unsigned int Width, unsigned int Height, unsigned int Pitch, unsigned int PixelSize)
{
	unsigned SrcPitch = Width * PixelSize;

	if (Pitch == SrcPitch)
	{
		// good - we can do a safe memcpy
		memcpy(pBuffer, pSrc, Pitch * Height);
	}
	else if (Pitch > SrcPitch)
	{
		// have to perform memcpys row-by-row
		unsigned char* pbyBuffer = (unsigned char*)pBuffer;
		unsigned char* pbySrc = (unsigned char*)pSrc;
		int idxBuffer, idxData;
		for (int i = 0; i < (int)Height; i++)
		{
			idxBuffer = i * Pitch;
			idxData = i * SrcPitch;
			memcpy(&pbyBuffer[idxBuffer], &pbySrc[idxData], SrcPitch);
		}
	}
	else
		return false; // is this condition even possible?

	return true;
}
